#include "pch.h"

#include "LevelLoader.h"
#include "WaveFrontObjLoader.h"
#include "BasicShapes.h"


using namespace Microsoft::WRL;
using namespace Windows::Storage;
using namespace Windows::Storage::FileProperties;
using namespace Windows::Storage::Streams;
using namespace Windows::Foundation;
using namespace Windows::ApplicationModel;

#define MAX_BUFFER_SIZE 255

WaveFrontObjLoader::WaveFrontObjLoader(ID3D11Device* device)
{
	this->device = device;
	this->basicReaderWriter = ref new BasicReaderWriter();

	this->shapesMaker = ref new BasicShapes(device);
}

// Opens a Wave Front file and returns an array of the shapes loaded from the file.
void WaveFrontObjLoader::LoadWaveFrontShapes(const wchar_t objFilePath[], Shape*** shapes, int* numberOfShapes)
{
	// load the data from the file
	Windows::Storage::StorageFolder^ storageForlder = Package::Current->InstalledLocation;
	Platform::String^ locationPath = Platform::String::Concat(storageForlder->Path, "\\");

	// Construct the full path to the file to load.
	Platform::String^ fullFilePath = Platform::String::Concat(locationPath, ref new Platform::String(objFilePath));

	// Open the file for reading
	HANDLE file =
		CreateFile2(
			fullFilePath->Data(),
			GENERIC_READ,
			FILE_SHARE_READ,
			OPEN_EXISTING,
			NULL);

	if (file != INVALID_HANDLE_VALUE)
	{
		ProcessWaveFrontData(file, shapes, numberOfShapes);

		// Finished with the File so close the handle.
		CloseHandle(file);
	}
}


void WaveFrontObjLoader::ProcessWaveFrontData(HANDLE file, Shape*** shapes, int *numberOfShapes)
{
	char* token = NULL;

	token = ReadNextToken(file);

	char* currentObjectName = NULL;
	List* currentListOfVertices = new List();
	List* currentListOfNormals = new List();
	List* currentListOfTextureCoords = new List();
	List* currentListOfIndices = new List();

	List* listOfShapes = new List();

	while (token != NULL)
	{
		printf("Token read: %s", token);

		if (token[0] == '#')
		{
			// free the memory of the last token that was read
			free(token);

			token = ReadToEndOfLineAndReadNextToken(file);
		}
		// Found Object Name
		else if (strcmp(token, "o") == 0)
		{
			free(token);

			char* newObjectName = ReadToEndOfLine(file);

			if (currentObjectName != NULL)
			{
				Shape* newShape = 
					CreateNewShape(
						currentObjectName,
						currentListOfVertices,
						currentListOfNormals,
						currentListOfTextureCoords,
						currentListOfIndices);

				listOfShapes->Add(newShape);


				// Clear the List of Indexes for the next shape
				currentListOfIndices->FreeThenClear();

				

			}

			currentObjectName = newObjectName;
			token = ReadNextToken(file);

			
		}
		else if (strcmp(token, "v") == 0)
		{
			free(token);
			float* vectorOfFloats = NULL;
			int numberOfFloats = 0;

			token = ReadListOfFloatsAndReadNextToken(file, &vectorOfFloats, &numberOfFloats);

			currentListOfVertices->Add(vectorOfFloats);
		}
		else if (strcmp(token, "vt") == 0)
		{
			free(token);
			float* vectorOfFloats = NULL;
			int numberOfFloats = 0;

			token = ReadListOfFloatsAndReadNextToken(file, &vectorOfFloats, &numberOfFloats);

			currentListOfTextureCoords->Add(vectorOfFloats);
		}
		else if (strcmp(token, "vn") == 0)
		{
			free(token);
			float* vectorOfFloats = NULL;
			int numberOfFloats = 0;

			token = ReadListOfFloatsAndReadNextToken(file, &vectorOfFloats, &numberOfFloats);

			currentListOfNormals->Add(vectorOfFloats);
		}
		else if (strcmp(token, "f") == 0)
		{
			// Found the indexes that is used to construct a Triangle.
			// read the indexes and store then in our WaveFrontTringle 
			// structure.
			free(token);
			WaveFrontTriangle *triangle = NULL;

			// Read the indexes into the WaveFrontTrinagle structure
			token = ReadListOfIndicesAndReadNextToken(file, &triangle);

			// If Read successfully then append the data into our list of indices.
			if (triangle != NULL)
			{
				currentListOfIndices->Add(triangle);
			}
		}
		else
		{
			free (token);

			token = ReadNextToken(file);
		}

	} 

	// Finished parse the file, build the last shape that was read from the file.
	if (currentObjectName != NULL)
	{
		Shape* newShape = 
			CreateNewShape(
				currentObjectName,
				currentListOfVertices,
				currentListOfNormals,
				currentListOfTextureCoords,
				currentListOfIndices);

		listOfShapes->Add(newShape);
	}

	// Fill the Out parameters with the Shapes read from the file
	(*shapes) = (Shape**)listOfShapes->ToArray();
	*numberOfShapes = listOfShapes->Count();

	// we don't need our list object any more so clear it and delete it
	listOfShapes->Clear();
	delete listOfShapes;

	// Free the resources that were allocated for parsing the file.
	ClearResources(
		currentListOfVertices,
		currentListOfNormals,
		currentListOfTextureCoords,
		currentListOfIndices);
}

//////////////////////////////////////////////////////////////////////////////////////////
// Reads a indices that defines a triangle returns the pointer to the WaveFrontTriangle
// structure in the out paramtere triangle.
char* WaveFrontObjLoader::ReadListOfIndicesAndReadNextToken(HANDLE file, WaveFrontTriangle **triangle)
{
	char* triangle1;
	char* triangle2;
	char* triangle3;

	triangle1 = ReadNextToken(file);

	if (triangle1 != NULL)
	{
		triangle2 = ReadNextToken(file);

		if (triangle2 != NULL)
		{
			triangle3 = ReadNextToken(file);

			if (triangle3 != NULL)
			{

				(*triangle) = (WaveFrontTriangle*)malloc(sizeof(WaveFrontTriangle));

				int v, t, n;

				SplitTriangleComponents(triangle1, &v, &t, &n);
				(*triangle)->vertex1 = v;
				(*triangle)->texture1 = t;
				(*triangle)->normal1 = n;

				SplitTriangleComponents(triangle2, &v, &t, &n);
				(*triangle)->vertex2 = v;
				(*triangle)->texture2 = t;
				(*triangle)->normal2 = n;

				SplitTriangleComponents(triangle3, &v, &t, &n);
				(*triangle)->vertex3 = v;
				(*triangle)->texture3 = t;
				(*triangle)->normal3 = n;




				return ReadNextToken(file);
			}
		}
	}

	return NULL;

}

////////////////////////////////////////////////////////////////////////////////////////////////
// Splits the string in the form:
// <VertexIndex>/<TextIndex>/<NormaIndex> into the integer component.
void WaveFrontObjLoader::SplitTriangleComponents(char *indexDesc, int* v, int* t, int* n)
{
	char strVertexIndex[10];
	char strTextureIndex[10];
	char strNormalIndex[10];

	memset(strVertexIndex, 0, sizeof(char) * 10);
	memset(strTextureIndex, 0, sizeof(char) * 10);
	memset(strNormalIndex, 0, sizeof(char) * 10);

	int i = 0;
	int j = 0;
	int start = 0;
	bool done = false;

	// read the indexDesc array until we reach the next '/' character.
	for(i = 0; i < (int)strlen(indexDesc) && !done;i ++)
	{
		if (indexDesc[i] != '/')
		{
			strVertexIndex[j] = indexDesc[i];
			j++;
		}
		else
		{
			done = true;
		}
	}

	done = false;
	start = i;
	j = 0;

	// read the indexDesc array until we reach the next '/' character.
	for(i = start; i < (int)strlen(indexDesc) && !done;i ++)
	{
		if (indexDesc[i] != '/')
		{
			strTextureIndex[j] = indexDesc[i];
			j++;
		}
		else
		{
			done = true;
		}
	}

	start = i;
	j = 0;

	for (i = start; i < (int)strlen(indexDesc);i ++)
	{
		strNormalIndex[j] = indexDesc[i];
		j ++;
	}

	sscanf_s(strVertexIndex, "%d", v);
	sscanf_s(strTextureIndex, "%d", t);
	sscanf_s(strNormalIndex, "%d", n);


}

/////////////////////////////////////////////////////////////////////////////////////////////////////
// Compiles a Directx Vertex and Index buffer from the Vertices, Text coords and Normals read from
// the file to construct a shape.
Shape* WaveFrontObjLoader::CreateNewShape(	
						char*	currentObjectName,
						List*	currentListOfVertices,
						List*	currentListOfNormals,
						List*	currentListOfTextureCoords,
						List*	currentListOfIndices)
{
	Shape* shape = new Shape();

	shape->Name = currentObjectName;

	unsigned int numberOfTriangles = currentListOfIndices->Count();
	unsigned int numberOfVertices = numberOfTriangles * 3;
	

	BasicVertex *vertices = (BasicVertex*)malloc(sizeof(BasicVertex) * numberOfVertices);
	unsigned short *indices = (unsigned short*)malloc(sizeof(unsigned short) * numberOfVertices);

	for(int i = 0; i < (int)numberOfTriangles; i ++)
	{
		float* v;
		float* n;
		float* t;

		int iv, in, it;

		iv = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->vertex3 - 1;
		in = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->normal3 - 1;
		it = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->texture3 - 1;

		v = (float*)currentListOfVertices->GetItem(iv);
		n = (float*)currentListOfNormals->GetItem(in);
		t = (float*)currentListOfTextureCoords->GetItem(it);

		vertices[i * 3].pos = float3(v[0], v[1], v[2]);
		vertices[i * 3].norm = float3(n[0], n[1], n[2]);
		vertices[i * 3].tex = float2(t[0], t[1]);

		indices[i * 3] = i * 3;

		iv = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->vertex2 - 1;
		in = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->normal2 - 1;
		it = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->texture2 - 1;

		v = (float*)currentListOfVertices->GetItem(iv);
		n = (float*)currentListOfNormals->GetItem(in);
		t = (float*)currentListOfTextureCoords->GetItem(it);

		vertices[(i * 3) + 1].pos = float3(v[0], v[1], v[2]);
		vertices[(i * 3) + 1].norm = float3(n[0], n[1], n[2]);
		vertices[(i * 3) + 1].tex = float2(t[0], t[1]);

		indices[(i * 3) + 1] = (i * 3) + 1;

		iv = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->vertex1 - 1;
		in = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->normal1 - 1;
		it = ((WaveFrontTriangle*)currentListOfIndices->GetItem(i))->texture1 - 1;

		v = (float*)currentListOfVertices->GetItem(iv);
		n = (float*)currentListOfNormals->GetItem(in);
		t = (float*)currentListOfTextureCoords->GetItem(it);

		vertices[(i * 3) + 2].pos = float3(v[0], v[1], v[2]);
		vertices[(i * 3) + 2].norm = float3(n[0], n[1], n[2]);
		vertices[(i * 3) + 2].tex = float2(t[0], t[1]);

		indices[(i * 3) + 2] = (i * 3) + 2;
	}

    this->shapesMaker->CreateVertexBuffer(
        numberOfVertices,
        vertices,
		&shape->VertexBuffer);

    this->shapesMaker->CreateIndexBuffer(
        numberOfVertices,
        indices,
		&shape->IndexBuffer);

	shape->IndexCount = numberOfVertices;

	shape->VertexStride = sizeof(BasicVertex);
	shape->VertexOffset = 0;

	shape->IndexFormat = DXGI_FORMAT_R16_UINT;
	shape->VertexTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;

	free(vertices);
	free(indices);

	return shape;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Clear the resources that were used in the process of parsing the file.
void WaveFrontObjLoader::ClearResources(
								List*	currentListOfVertices,
								List*	currentListOfNormals,
								List*	currentListOfTextureCoords,
								List*	currentListOfIndices)
{
	
	currentListOfVertices->FreeThenClear();
	delete currentListOfVertices;

	currentListOfNormals->FreeThenClear();
	delete currentListOfNormals;

	currentListOfTextureCoords->FreeThenClear();
	delete currentListOfTextureCoords;

	currentListOfIndices->FreeThenClear();
	delete currentListOfIndices;

}

/////////////////////////////////////////////////////////////////////////////////////
// Read the next token from the Wave Front file.
char* WaveFrontObjLoader::ReadNextToken(HANDLE file)
{
	char buffer[MAX_BUFFER_SIZE];
	char readbuffer[1];

	memset(buffer, 0, sizeof(char) * MAX_BUFFER_SIZE);

	DWORD itemsRead;

	ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);

	// Keep reading the file until we reach something interesting,
	// ie.  throw away white characters.
	while (itemsRead > 0 &&
				(!IsLetter (readbuffer[0]) &&
				 !IsNumber (readbuffer[0]) &&
				 !IsSymbol (readbuffer[0]) &&
				 !IsHash (readbuffer[0])))
	{
		ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);
	}

	if (itemsRead > 0)
	{
		

		int i = 1;
		buffer[0] = readbuffer[0];
		ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);


		while (	itemsRead > 0 &&
				(IsLetter (readbuffer[0]) ||
				 IsNumber (readbuffer[0]) ||
				 IsSymbol (readbuffer[0]) ||
				 IsHash (readbuffer[0])))
		{
			buffer[i] = readbuffer[0];

			i ++;
			ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);
		}

		int sizeOfReturnBuffer = strlen(buffer) + 1;

		char* returnValue = (char*)malloc(sizeOfReturnBuffer * sizeof(char));

		memset(returnValue, 0, sizeof(char) * sizeOfReturnBuffer);

		int size = strlen(buffer);

		memcpy_s(returnValue, size, buffer, size);
		//strcpy_s(returnValue, size, buffer);

		return returnValue;
	}
	else
	{
		return NULL;
	}
}

bool WaveFrontObjLoader::IsLetter(char c)
{
	return isalpha((int)c) != 0;
}

bool WaveFrontObjLoader::IsNumber(char c)
{
	return isdigit((int)c) != 0;
}

bool WaveFrontObjLoader::IsSymbol(char c)
{
	return c == '.' || c == '/' || c == '-' || c == '_';
}

bool WaveFrontObjLoader::IsHash(char c)
{
	return c == '#';
}

bool WaveFrontObjLoader::IsEOFL(char c)
{
	return c == '\r' || c == '\n';
}

char* WaveFrontObjLoader::ReadToEndOfLineAndReadNextToken(HANDLE file)
{
	char readbuffer[1];
	DWORD itemsRead;

	ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);

	// Keep reading the file until we reach an end of line character.
	while (itemsRead > 0 &&
		!IsEOFL(readbuffer[0]))
	{
		ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);
	}

	// Read and return the next token in the file.
	return ReadNextToken(file);
}

char* WaveFrontObjLoader::ReadToEndOfLine(HANDLE file)
{
	char buffer[MAX_BUFFER_SIZE];
	char readbuffer[1];
	DWORD itemsRead;

	// initialize the buffer to blank
	memset(buffer, 0, sizeof(char) * MAX_BUFFER_SIZE);

	ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);

	// Keep reading until we reach something interesting.
	while (itemsRead > 0 &&
			(!IsLetter (readbuffer[0]) &&
			!IsNumber (readbuffer[0]) &&
			!IsSymbol (readbuffer[0]) &&
			!IsHash (readbuffer[0])))
	{
		ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);
	}

	if (itemsRead > 0)
	{
		int i = 1;
		buffer[0] = readbuffer[0];
		ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);


		while (	itemsRead > 0 &&
				!IsEOFL(readbuffer[0]))
		{
			buffer[i] = readbuffer[0];

			i ++;
			ReadFile(file, readbuffer, sizeof(char) * 1, &itemsRead, NULL);
		}

		int sizeOfReturnBuffer = strlen(buffer) + 1;

		char* returnValue = (char*)malloc(sizeOfReturnBuffer * sizeof(char));

		memset(returnValue, 0, sizeof(char) * sizeOfReturnBuffer);

		int size = strlen(buffer);

		memcpy_s(returnValue, size, buffer, size);
		//strcpy_s(returnValue, size, buffer);

		return returnValue;
	}
	else
	{
		return NULL;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
// Tests if the given string is a floating point number
bool WaveFrontObjLoader::IsFloat(char* str)
{
	int length = strlen(str);
	bool hasDecimal = false;
	bool isFloat = true;

	for (int i = 0; i < length && isFloat; i ++)
	{
		if (i == 0 && str[i] == '-')
		{

		}
		else if (IsNumber(str[i]))
		{

		}
		else if (hasDecimal == false && str[i] == '.')
		{
			hasDecimal = true;
		}
		else
		{
			// this is not a floatig point number
			isFloat = false;
		}
	}

	return isFloat;
}

///////////////////////////////////////////////////////////////////////////////////////////////
// Reads an array of floats from the file until it reaches a NON float token or the end of file.
char* WaveFrontObjLoader::ReadListOfFloatsAndReadNextToken(HANDLE file, float **vectorOfFloats, int* totalNumberOfFloats)
{
	int numberOfFloats = 0;
	float arrayOfFloats[20];

	// Read a token from the file
	char* token = ReadNextToken(file);

	// If the last token read is a Floating polint number in 
	// string format, then
	while (IsFloat(token))
	{
		// Get the float from the string
		float f;

		sscanf_s(token, "%f", &f);

		// apend the float to our array of floats.
		arrayOfFloats[numberOfFloats] = f;
		numberOfFloats ++;

		// free the token, we don't need it any more
		free(token);

		// Get the next token from the file.
		token = ReadNextToken(file);
	}

	// Store the number of floats read
	*totalNumberOfFloats = numberOfFloats;

	// if we read some floats from the file
	if (numberOfFloats > 0)
	{
		// Then allocated a dynamic array to store the floats read.
		(*vectorOfFloats) = (float*)malloc(sizeof(float) * numberOfFloats);

		// store the floats read ito the array.
		for(int i = 0; i < numberOfFloats; i ++)
		{
			(*vectorOfFloats)[i] = arrayOfFloats[i];
		}
	}

	return token;
}